;; protocol/osc-read.scm - (c) rohan drape, 2002-2005

;; Since a pure decode/scan without shared u8vectors is very
;; inefficient, implement osc-read/scan-* forms.

;; Evaluates to the string encoded at the OSC byte stream `port'.

(define (osc-read/scan-string port)
  (let loop ((b (read-u8vector 4 port))
	     (r (list)))
    (if (eq? 0 (u8vector-ref b 3))
	(osc-decode/scan-string (u8vector-concatenate (reverse (cons b r))))
	(loop (read-u8vector 4 port) (cons b r)))))

;; Evaluates to the byte string encoded at the OSC byte stream `port'.

(define (osc-read/scan-bytes port)
  (let* ((n (read-int32 port))
	 (b (read-u8vector n port)))
    (read-u8vector (- 4 (modulo n 4)) port)
    b))

;; Evaluates to the object, described by the OSC type character
;; `type', encoded at the OSC byte stream `port'.

(define (osc-read/scan-value type port)
  (cond ((eq? type #\i)
	 (read-int32 port))
	((eq? type #\t)
	 (read-uint64 port))
	((eq? type #\h)
	 (read-int64 port))
	((eq? type #\f)
	 (read-float32 port))
	((eq? type #\d)
	 (read-float64 port))
	((eq? type #\s)
	 (osc-read/scan-string port))
	((eq? type #\b)
	 (osc-read/scan-bytes port))
	(else
	 (error "osc-read/scan-value: Bad type character" type))))

;; Evaluate to the list of objects encoded at the OSC byte stream
;; `port', conforming to the types given in the OSC character type
;; list `types'.

(define (osc-read/scan-arguments types port)
  (if (null? types)
      '()
      (cons (osc-read/scan-value (car types) port)
	    (osc-read/scan-arguments (cdr types) port))))

;; Evaluate to the scheme representation of the OSC message at the OSC
;; byte stream `port'.  The first object is the 'address' of the
;; message, any subsequent objects are arguments for that address.

(define (osc-read/scan-message port)
  (let* ((address (osc-read/scan-string port))
	 (type-descriptor (osc-read/scan-string port)))
    (cons address
	  (osc-read/scan-arguments (cdr (string->list type-descriptor)) port))))

;; Evaluate to a scheme representation of the OSC bundle encoded at
;; the OSC stream `port'.  The bundle ends at the end of the byte
;; stream.  The first object is the <real> UTC 'timetag' of the
;; bundle, any subsequent objects are either OSC messages or embedded
;; OSC bundles.

(define (osc-read/scan-bundle port)
  (let ((bundletag (osc-read/scan-string port))
	(timetag (ntp->utc. (read-uint64 port)))
	(parts (list)))
    
    ;; Report illegal bundle.
    (if (not (equal? bundletag "#bundle"))
	(error "osc-read/scan-bundle: Illegal bundle tag" bundletag))

    (cons timetag
	  (let loop ((parts (list)))
	    (if (eof-object? (peek-char port)) 
		(reverse parts)
		(begin
		  ;; We have no use for the message size...
		  (read-int32 port)
		  (loop (cons (osc-read/scan-packet port) parts))))))))

;; Evaluate to the scheme representation of the OSC packet encoded at
;; the OSC byte stream `port'. An OSC packet is either an OSC message
;; or an OSC bundle.

(define (osc-read/scan-packet port)
  (if (eq? (peek-char port) #\#)
      (osc-read/scan-bundle port)
      (osc-read/scan-message port)))

